001 /*
002 * Copyright 2006 Stephen J. McConnell.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
013 * implied.
014 *
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018
019 package net.dpml.metro.builder;
020
021 import java.io.IOException;
022 import java.io.OutputStream;
023 import java.io.OutputStreamWriter;
024 import java.io.Writer;
025 import java.net.URI;
026
027 import javax.xml.XMLConstants;
028
029 import net.dpml.component.ActivationPolicy;
030 import net.dpml.component.Directive;
031
032 import net.dpml.lang.Value;
033 import net.dpml.lang.ValueEncoder;
034
035 import net.dpml.metro.data.ContextDirective;
036 import net.dpml.metro.data.CategoryDirective;
037 import net.dpml.metro.data.CategoriesDirective;
038 import net.dpml.metro.data.ComponentDirective;
039 import net.dpml.metro.data.LookupDirective;
040 import net.dpml.metro.data.ValueDirective;
041
042 import net.dpml.metro.info.LifestylePolicy;
043 import net.dpml.metro.info.CollectionPolicy;
044 import net.dpml.metro.info.PartReference;
045 import net.dpml.metro.info.Priority;
046
047 import net.dpml.util.Encoder;
048
049 /**
050 * Component part handler.
051 *
052 * @author <a href="http://www.dpml.net">Digital Product Meta Library</a>
053 * @version 1.0.1
054 */
055 public class ComponentEncoder extends ComponentConstants implements Encoder
056 {
057 private static final String XML_HEADER = "<?xml version=\"1.0\"?>";
058 private static final String TYPE_SCHEMA_URN = "link:xsd:dpml/lang/dpml-type#1.0";
059 private static final String STATE_SCHEMA_URN = "link:xsd:dpml/lang/dpml-state#1.0";
060 private static final String PART_SCHEMA_URN = "link:xsd:dpml/lang/dpml-part#1.0";
061 private static final String COMPONENT_SCHEMA_URN = "link:xsd:dpml/lang/dpml-component#1.0";
062 private static final String PARTIAL_COMPONENT_HEADER =
063 "<component xmlns=\""
064 + COMPONENT_SCHEMA_URN
065 + "\""
066 + "\n xmlns:xsi=\""
067 + XMLConstants.W3C_XML_SCHEMA_INSTANCE_NS_URI
068 + "\"\n xmlns:part=\""
069 + PART_SCHEMA_URN
070 + "\"\n xmlns:type=\""
071 + TYPE_SCHEMA_URN
072 + "\"\n xmlns:component=\""
073 + COMPONENT_SCHEMA_URN
074 + "\"";
075
076 private static final ValueEncoder VALUE_ENCODER = new ValueEncoder();
077
078 /**
079 * Export a component directive to an output stream as XML.
080 * @param directive the component directive
081 * @param output the output stream
082 * @exception IOException if an IO error occurs
083 */
084 public void export( ComponentDirective directive, OutputStream output ) throws IOException
085 {
086 final Writer writer = new OutputStreamWriter( output );
087
088 writer.write( XML_HEADER );
089 writer.write( "\n\n" );
090 writer.write( PARTIAL_COMPONENT_HEADER );
091 writeAttributes( writer, directive, "" );
092 writeBody( writer, directive, " " );
093 writer.write( "\n" );
094 writer.write( "</component>" );
095 writer.write( "\n" );
096 writer.flush();
097 output.close();
098 }
099
100 /**
101 * Export a component directive to an output stream as XML.
102 * @param writer the print writer
103 * @param object the object to encode
104 * @param pad character offset
105 * @exception IOException if an IO error occurs
106 */
107 public void encode( Writer writer, Object object, String pad ) throws IOException
108 {
109 if( object instanceof ComponentDirective )
110 {
111 writeTaggedComponent( writer, (ComponentDirective) object, null, pad, true );
112 }
113 else
114 {
115 final String error =
116 "Encoding subject is not recognized."
117 + "\nClass: " + object.getClass().getName();
118 throw new IllegalArgumentException( error );
119 }
120 }
121
122 /**
123 * Export a component directive to an output stream as XML.
124 * @param writer the print writer
125 * @param directive the component directive
126 * @param pad character offset
127 * @exception IOException if an IO error occurs
128 */
129 public void writeComponent(
130 Writer writer, ComponentDirective directive, String pad ) throws IOException
131 {
132 writeTaggedComponent( writer, directive, null, pad );
133 }
134
135 /**
136 * Export a tagged component directive to an output stream as XML.
137 * @param writer the print writer
138 * @param directive the component directive
139 * @param key the key identifying the component
140 * @param pad character offset
141 * @exception IOException if an IO error occurs
142 */
143 public void writeTaggedComponent(
144 Writer writer, ComponentDirective directive, String key, String pad ) throws IOException
145 {
146 writeTaggedComponent( writer, directive, key, pad, true );
147 }
148
149 /**
150 * Export a tagged component directive to an output stream as XML.
151 * @param writer the print writer
152 * @param directive the component directive
153 * @param key the key identifying the component
154 * @param pad character offset
155 * @param flag true if the xml namespace should be included
156 * @exception IOException if an IO error occurs
157 */
158 public void writeTaggedComponent(
159 Writer writer, ComponentDirective directive, String key, String pad, boolean flag ) throws IOException
160 {
161 writer.write( "\n" + pad + "<component" );
162 if( flag )
163 {
164 writer.write( " xmlns=\"" + COMPONENT_SCHEMA_URN + "\"" );
165 }
166 if( null != key )
167 {
168 writer.write( " key=\"" + key + "\"" );
169 }
170 writer.write( "\n" + pad + " " );
171 writeAttributes( writer, directive, pad + " " );
172 writeBody( writer, directive, pad + " " );
173 writer.write( "\n" + pad + "</component>" );
174 }
175
176 void writeAttributes(
177 Writer writer, ComponentDirective directive, String pad ) throws IOException
178 {
179 String classname = directive.getClassname();
180 if( null != classname )
181 {
182 writer.write( " type=\"" + classname + "\"" );
183 }
184 URI uri = directive.getBaseURI();
185 if( null != uri )
186 {
187 writer.write( " uri=\"" + uri.toASCIIString() + "\"" );
188 }
189 String name = directive.getName();
190 if( null != name )
191 {
192 writer.write( "\n" + pad + " name=\"" + name + "\"" );
193 }
194 LifestylePolicy lifestyle = directive.getLifestylePolicy();
195 if( null != lifestyle )
196 {
197 writer.write( "\n" + pad + " lifestyle=\"" + lifestyle.getName() + "\"" );
198 }
199 CollectionPolicy collection = directive.getCollectionPolicy();
200 if( null != collection )
201 {
202 writer.write( "\n" + pad + " collection=\"" + collection.getName() + "\"" );
203 }
204 ActivationPolicy activation = directive.getActivationPolicy();
205 if( null != activation )
206 {
207 writer.write( "\n" + pad + " activation=\"" + activation.getName() + "\"" );
208 }
209 writer.write( ">" );
210 }
211
212 void writeBody(
213 Writer writer, ComponentDirective directive, String pad ) throws IOException
214 {
215 CategoriesDirective categories = directive.getCategoriesDirective();
216 ContextDirective context = directive.getContextDirective();
217 PartReference[] parts = directive.getPartReferences();
218 writeCategoriesDirective( writer, categories, pad );
219 writeContextDirective( writer, context, pad );
220 writeParts( writer, parts, pad, false );
221 }
222
223 private void writeCategoriesDirective(
224 Writer writer, CategoriesDirective categories, String pad ) throws IOException
225 {
226 if( null == categories )
227 {
228 return;
229 }
230
231 String name = categories.getName();
232 Priority priority = categories.getPriority();
233 String target = categories.getTarget();
234 CategoryDirective[] subCategories = categories.getCategories();
235
236 if( isaNullValue( name ) && isaNullPriority( priority ) && isaNullValue( target )
237 && ( subCategories.length == 0 ) )
238 {
239 return;
240 }
241
242 writer.write( "\n" + pad + "<categories" );
243 if( !isaNullValue( name ) )
244 {
245 writer.write( " name=\"" + name + "\"" );
246 }
247 if( !isaNullPriority( priority ) )
248 {
249 writer.write( " priority=\"" + priority.getName() + "\"" );
250 }
251 if( !isaNullValue( target ) )
252 {
253 writer.write( " target=\"" + target + "\"" );
254 }
255 if( subCategories.length == 0 )
256 {
257 writer.write( "/>" );
258 }
259 else
260 {
261 writer.write( ">" );
262 for( int i=0; i<subCategories.length; i++ )
263 {
264 CategoryDirective directive = subCategories[i];
265 if( directive instanceof CategoriesDirective )
266 {
267 CategoriesDirective c = (CategoriesDirective) directive;
268 writeCategoriesDirective( writer, c, pad + " " );
269 }
270 else
271 {
272 writeCategoryDirective( writer, directive, pad + " " );
273 }
274 }
275 writer.write( "\n" + pad + "</categories>" );
276 }
277 }
278
279 private boolean isaNullPriority( Priority priority )
280 {
281 if( null == priority )
282 {
283 return true;
284 }
285 else
286 {
287 return Priority.DEBUG.equals( priority );
288 }
289 }
290
291 private boolean isaNullValue( String value )
292 {
293 if( null == value )
294 {
295 return true;
296 }
297 else
298 {
299 return "".equals( value );
300 }
301 }
302
303 private void writeCategoryDirective(
304 Writer writer, CategoryDirective category, String pad ) throws IOException
305 {
306 String name = category.getName();
307 Priority priority = category.getPriority();
308 String target = category.getTarget();
309
310 writer.write( "\n" + pad + "<category" );
311 if( null != name )
312 {
313 writer.write( " name=\"" + name + "\"" );
314 }
315 if( null != priority )
316 {
317 writer.write( " priority=\"" + priority.getName() + "\"" );
318 }
319 if( null != target )
320 {
321 writer.write( " target=\"" + target + "\"" );
322 }
323 writer.write( "/>" );
324 }
325
326 private void writeContextDirective(
327 Writer writer, ContextDirective context, String pad ) throws IOException
328 {
329 if( null == context )
330 {
331 return;
332 }
333
334 String classname = context.getClassname();
335 PartReference[] parts = context.getDirectives();
336
337 if( ( null == classname ) && ( parts.length == 0 ) )
338 {
339 return;
340 }
341
342 writer.write( "\n" + pad + "<context" );
343 if( null != classname )
344 {
345 writer.write( " class=\"" + classname + "\"" );
346 }
347 if( parts.length == 0 )
348 {
349 writer.write( "/>" );
350 }
351 else
352 {
353 writer.write( ">" );
354 writeContextEntries( writer, parts, pad + " " );
355 writer.write( "\n" + pad + "</context>" );
356 }
357 }
358
359 /**
360 * Write a collection of part references.
361 * @param writer the writer
362 * @param parts the part refernece array
363 * @param pad the offset
364 * @param flag true if the xml namespace should be included
365 * @exception IOException if an IO error occurs
366 */
367 protected void writeParts(
368 Writer writer, PartReference[] parts, String pad, boolean flag ) throws IOException
369 {
370 if( null == parts )
371 {
372 return;
373 }
374
375 if( parts.length == 0 )
376 {
377 return;
378 }
379 else
380 {
381 writer.write( "\n" + pad + "<parts>" );
382 writePartReferences( writer, parts, pad + " ", flag );
383 writer.write( "\n" + pad + "</parts>" );
384 }
385 }
386
387 private void writePartReferences(
388 Writer writer, PartReference[] parts, String pad, boolean flag ) throws IOException
389 {
390 for( int i=0; i<parts.length; i++ )
391 {
392 PartReference ref = parts[i];
393 writePartReference( writer, ref, pad, flag );
394 }
395 }
396
397 private void writeContextEntries(
398 Writer writer, PartReference[] parts, String pad ) throws IOException
399 {
400 for( int i=0; i<parts.length; i++ )
401 {
402 PartReference ref = parts[i];
403 writeContextEntry( writer, ref, pad );
404 }
405 }
406
407 private void writeContextEntry(
408 Writer writer, PartReference part, String pad ) throws IOException
409 {
410 String key = part.getKey();
411 if( null == key )
412 {
413 throw new IllegalStateException( "key" );
414 }
415 Directive directive = part.getDirective();
416 if( null == directive )
417 {
418 throw new IllegalStateException( "directive" );
419 }
420 if( directive instanceof ValueDirective )
421 {
422 ValueDirective value = (ValueDirective) directive;
423 writeEntry( writer, key, value, pad );
424 }
425 else if( directive instanceof LookupDirective )
426 {
427 LookupDirective value = (LookupDirective) directive;
428 writeLookupEntry( writer, key, value, pad );
429 }
430 else
431 {
432 String classname = directive.getClass().getName();
433 final String message = "WARNING: UNRECOGNIZED ENTRY: "+ classname;
434 System.out.println( "# " + message );
435 System.out.println( "# key: " + key );
436 System.out.println( "# class: " + classname );
437 writer.write( "\n" + pad + "<!-- " + message + " -->" );
438 writer.write( "\n" + pad + "<!-- " );
439 writer.write( "\n" + pad + "key: " + key );
440 writer.write( "\n" + pad + "class: " + directive.getClass().getName() );
441 writer.write( "\n" + pad + "-->" );
442 }
443 }
444
445 private void writeLookupEntry(
446 Writer writer, String key, LookupDirective directive, String pad ) throws IOException
447 {
448 String classname = directive.getServiceClassname();
449 writer.write( "\n" + pad + "<entry key=\"" + key + "\" lookup=\"" + classname + "\"/>" );
450 }
451
452 private void writePartReference(
453 Writer writer, PartReference part, String pad, boolean flag ) throws IOException
454 {
455 String key = part.getKey();
456 if( null == key )
457 {
458 throw new IllegalStateException( "key" );
459 }
460
461 Directive directive = part.getDirective();
462 if( null == directive )
463 {
464 throw new IllegalStateException( "directive" );
465 }
466 if( directive instanceof ComponentDirective )
467 {
468 ComponentDirective component = (ComponentDirective) directive;
469 writeTaggedComponent( writer, component, key, pad, flag );
470 }
471 else
472 {
473 String classname = directive.getClass().getName();
474 final String error =
475 "Part reference directive class not recognized."
476 + "\nClass: " + classname;
477 throw new IllegalArgumentException( error );
478 }
479 }
480
481 /**
482 * Write a context entry.
483 * @param writer the writer
484 * @param key the entry key
485 * @param value the value directive
486 * @param pad the offset
487 * @exception IOException if an IO error occurs
488 */
489 protected void writeEntry( Writer writer, String key, ValueDirective value, String pad ) throws IOException
490 {
491 String target = value.getTargetExpression();
492 String method = value.getMethodName();
493
494 writer.write( "\n" + pad + "<entry key=\"" + key + "\"" );
495 if( null != target )
496 {
497 writer.write( " class=\"" + target + "\"" );
498 }
499 if( null != method )
500 {
501 writer.write( " method=\"" + method + "\"" );
502 }
503 Value[] values = value.getValues();
504 if( values.length > 0 )
505 {
506 writer.write( ">" );
507 VALUE_ENCODER.encodeValues( writer, values, pad + " " );
508 writer.write( "\n" + pad + "</entry>" );
509 }
510 else
511 {
512 String v = value.getBaseValue();
513 if( null != v )
514 {
515 writer.write( " value=\"" + v + "\"" );
516 }
517 writer.write( "/>" );
518 }
519 }
520 }